#include "Physics.h"
#include "GameSystemManager.h"
#include "GameSceneManager.h"
#include "ETTerrainManager.h"
#include "GameObjectManager.h"
#include "CMesh.h"
#include "CRender.h"
#include "CPhysics.h"

PhysicsConfig physics_config;

/*
if(GameSystemManager::getSingleton()->getGUI()->isCheckBoxSelected("Physics/chkTerrain")) {
	physics_config.isTerrainActorLoaded = true;
	createTerrain();
}
*/

//Create the terrain Actor based on data gathered from ETM

void PhysicsSystem::createTerrainActor()
{
	NxHeightFieldDesc mNxHeightFieldDesc;
	
	//ET:: GameSystemManager::getSingleton()->getTerrain()->getTerrainManager()
	
	ET::TerrainManager * tMgr = GameSystemManager::getSingleton()->getTerrain()->getTerrainManager(); //)->getTerrainManager();

	//ET::TerrainManager * tMgr = GameSystemManager::getSingleton()->getTerrain()->getTerrainManager();
	mNxHeightFieldDesc.nbColumns = (NxU32)tMgr->getTerrainInfo().getHeight();
    mNxHeightFieldDesc.nbRows    = (NxU32)tMgr->getTerrainInfo().getWidth();
    mNxHeightFieldDesc.verticalExtent      = -1000;
    mNxHeightFieldDesc.convexEdgeThreshold = 0;
	
    //Allocate storage for data
    mNxHeightFieldDesc.samples      = new NxU32[mNxHeightFieldDesc.nbColumns * mNxHeightFieldDesc.nbRows];
    mNxHeightFieldDesc.sampleStride = sizeof(NxU32);

    NxU8* currentByte = (NxU8*)mNxHeightFieldDesc.samples;
    ET::TerrainInfo i = tMgr->getTerrainInfo();
    std::vector<float> heightData = i.getHeightmapData();
      
    for (NxU32 row = 0; row < mNxHeightFieldDesc.nbRows; row++)
    {
		for (NxU32 column = 0; column < mNxHeightFieldDesc.nbColumns; column++)
		{
			NxHeightFieldSample* currentSample = (NxHeightFieldSample*)currentByte;

			//Transform ETM array of floats into NxHeightFieldSample
			currentSample->height         = static_cast<NxI16>(((i.at(row,column)) * 65535) - 32768);
			currentSample->materialIndex0 = 0;
			currentSample->materialIndex1 = 0;
            
			currentSample->tessFlag = 0;
            
			currentByte += mNxHeightFieldDesc.sampleStride;
		}
    }
	NxHeightField *mHeightField;
    mHeightField = mWorld->getSDK()->createHeightField(mNxHeightFieldDesc);
	
    delete[] mNxHeightFieldDesc.samples;
	
    Ogre::AxisAlignedBox aab = tMgr->getTerrainInfo().getExtents(); // getDimensions();
    Ogre::Vector3 s = aab.getSize();

    NxVec3 size;
    size.x = s.x;
    size.y = s.y;
    size.z = s.z;

    NxHeightFieldShapeDesc heightFieldShapeDesc;
    heightFieldShapeDesc.heightField   = mHeightField;
    heightFieldShapeDesc.shapeFlags      = NX_SF_FEATURE_INDICES | NX_SF_VISUALIZATION;
    heightFieldShapeDesc.heightScale   = size.y / 65536.0f;
    heightFieldShapeDesc.rowScale      = size.x / NxReal(mNxHeightFieldDesc.nbRows-1);
    heightFieldShapeDesc.columnScale   = size.z / NxReal(mNxHeightFieldDesc.nbColumns-1);
    //heightFieldShapeDesc.meshFlags   = NX_MESH_SMOOTH_SPHERE_COLLISIONS;
    heightFieldShapeDesc.materialIndexHighBits = 0;
    heightFieldShapeDesc.holeMaterial = 2;
    heightFieldShapeDesc.localPose.t = NxVec3(0,0,0);
    Ogre::Vector3 offset = tMgr->getTerrainInfo().getOffset();
	heightFieldShapeDesc.localPose.t = NxVec3(offset.x,0,offset.z); // AABB1

    NxActorDesc actorDesc;
    actorDesc.shapes.pushBack(&heightFieldShapeDesc);
    actorDesc.density   = 1.0f;
    actorDesc.globalPose.t = NxVec3(0,size.y/2,0);

    //nWorld->createActor(actorDesc); 
	mScene->getNxScene()->createActor(actorDesc);
	
	//mScene->createActor("t",new NxOgre::Terrain(mHeightField,size,)		)

	/*
	mScene->createActor("Trotters",	new Terrain(mHeightField, float3(128,4,128)),
						"",	"centering: xz-above, hole-material: 65535"),
						Ogre::Vector3(64, 0, 64), "static: yes");
	*/

	//mWorld->simulate()
	
	
	

	//mWorld

	//mScene->createActor()

	/*
	mScene->createActor("TerrainActor", new NxOgre::Terrain(mHeightField, float3(1500, 50, 1500), sp, tp),
		NxVec3(0, 50, 0), "static: yes");		
	*/

	return;
}


PhysicsSystem::PhysicsSystem() {
	//startSimulation = false;
	physics_config.isSimulation = false;
	
	mWorld = new World("time-controller: ogre");
	
	mWorld->getSDK()->getFoundationSDK().getRemoteDebugger()->connect("127.0.0.1");

	mIsInitialized = true;
	mIsRunningSim = false;

	//physicsSDK->getFoundationSDK().getRemoteDebugger()->connect("127.0.0.1");
	//initializePhysx();
}
PhysicsSystem::~PhysicsSystem() {
	//destroyPhysx();
}

void PhysicsSystem::createScene() {

	mScene = mWorld->createScene("Main", GameSceneManager::getSingleton()->getSceneManager()
		, "gravity: yes, floor: no, renderer: ogre");

	//Body *mCube = mScene->createBody("cube;cube.1m.mesh", new Cube(1), Vector3(600,400,280), "mass: 10");
	
	//mCube->setGlobalPosition(Vector3(600,400,280));

	createTerrainActor();

}

void PhysicsSystem::startSimulation() {
	if(mWorld)
		mIsRunningSim = true;
	//else
		//Display error saying cant start sim without initialazing PhysX
		
}
void PhysicsSystem::stopSimulation() {
	if(mWorld)
		mIsRunningSim = false;
}

//Destroy all Nx objects of the current world
void PhysicsSystem::resetScene() {
	//resetScene();
}

void PhysicsSystem::simulate(const Ogre::Real &time)
{
	//updatePhysics(time);
	//mScene->simulate(time);
	if(mIsInitialized && mIsRunningSim && mWorld)
		mWorld->simulate(time);
}

void PhysicsSystem::setDefaultConfig()
{
	physics_config.Gravity = 9.8f;
	physics_config.Skin = 1.0f;
	physics_config.Restitution = 0.8f;
	physics_config.StaticFriction = 0.05f;
	physics_config.DynamicFriction = 0.8f;
}

NxOgre::World* PhysicsSystem::getWorld()
{
	return mWorld;
}
NxOgre::Scene* PhysicsSystem::getScene()
{
	return mScene;
}

void PhysicsSystem::shutdown()
{
	//destroyPhysx();
}